package org.jheuristics.ga.operators.reproductors;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.epanetgrid.otimizacao.util.OtimizacaoUtil;
import org.jheuristics.Individual;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.operators.Probability;
import org.jheuristics.ga.operators.Reproductor;
import org.jheuristics.util.BitString;


/**
 * TODO
 *
 * @author Marcell Manfrin, marcell at ourgrid.org, 2005
 */
public class BitStringGensListMutationVinicius extends AbstractGensListMutation {

	private double porcBombasMutadas;

	/**
	 * TODO
	 *
	 * @param probability
	 * @param tryOuts
	 */
	public BitStringGensListMutationVinicius(Probability probability, int tryOuts, double porcBombasMutadas) {
		super(probability, tryOuts);
		this.porcBombasMutadas = porcBombasMutadas;
	}

	/**
	 * TODO
	 *
	 * @param decoretedReproductor
	 * @param probability
	 * @param tryOuts
	 */
	public BitStringGensListMutationVinicius(Reproductor decoretedReproductor, Probability probability, int tryOuts) {
		super(decoretedReproductor, probability, tryOuts);
	}

	/**
	 * TODO
	 *
	 * @param individual
	 * @param status
	 * @param config
	 * @return
	 * @see org.jheuristics.ga.operators.reproductors.AbstractMutation#mutate(org.jheuristics.Individual, org.jheuristics.ga.GAStatus, org.jheuristics.ga.GAConfig)
	 */
	protected Individual mutate(Individual individual, GAStatus status, GAConfig config) {
		List<BitString> gens = (List<BitString>) individual.getGens();
		
//		if(gens.get(0).size() < porcaoBombas) {
//			throw new IllegalArgumentException();
//		}
		
		StringBuffer buffer = new StringBuffer();
		
		buffer.append("Cromossomo enviado para Muta��o Normal (intervalo): ");
		buffer.append(individual);
		buffer.append("\n");
		
		int intervalo = config.getRandomGenerator().nextInt(gens.size());
		
		buffer.append("INTERVALO SELECIONADO: ");
		buffer.append(intervalo+1);
		buffer.append("\n");
		
		Object genMutaded = mutate(gens, status, config, intervalo);
		
		logger.debug(buffer);
		buffer = new StringBuffer();
        // did mutation happen?
        if (null != genMutaded) {
        	buffer.append("Cromossomo mutado: ");
    		buffer.append(OtimizacaoUtil.gensToString((List<BitString>) genMutaded));
    		buffer.append("\n");
    		logger.debug(buffer);
            
    		Individual mutado = config.getIndividualsFactory().createSpecificIndividual(genMutaded, config);
    	    
    		//FIXME: possivel corre��o para a perda do data application de individuos j� simulados
    	    
    	    if(mutado.getGens().equals(individual.getGens())){
    	    	mutado.setDataApplication(individual.getDataApplication());
    	    	mutado.setFitness(individual.getFitness());
    	    }

    	    ///////////////////////////////
            return mutado;
    		
        } else {
        	buffer.append("Cromossomo n�o sofreu muta��o: ");
    		buffer.append("\n");
    		logger.debug(buffer);
            return individual;
        }
	}
	
	protected Object mutate(List<BitString> objectGens, GAStatus status, GAConfig config, int intervalo) {
		int numBombas = objectGens.get(0).length();
		double quantBombasMutadas = Math.ceil(numBombas * porcBombasMutadas);
    	Probability probability = getProbability();
		List<BitString> result = new LinkedList<BitString>();
		for (int i = 0; i < objectGens.size(); i++) {
			result.add(i, new BitString(objectGens.get(i)));
		}
		
		Set<Integer> bombasASeremMutadas = new HashSet<Integer>();
		
		while (bombasASeremMutadas.size() != quantBombasMutadas) {
			bombasASeremMutadas.add(config.getRandomGenerator().nextInt(numBombas));
		}
		
		if (probability.check(status, config)) {
			BitString mutado = new BitString(objectGens.get(intervalo));
			for (Integer integer : bombasASeremMutadas) {
				mutado.flip(integer);
			}
			result.set(intervalo, mutado);
		}
		return result;
    }
	
    /**
     * TODO
     *
     * @param gens
     * @param status
     * @param config
     * @return
     */
    protected Object mutate(Object objectGens, GAStatus status, GAConfig config) {
//    	Probability probability = getProbability();
//		BitString string = (BitString) objectGens;
//		boolean changed = false;
//		
//		for (int i = 0; i < string.length(); i++) {
//			if (probability.check(status, config)) {
//				if (!changed) {
//					changed = true;
//					string = new BitString(string);
//				}
//				string.flip(i);
//				
//			}
//		}
//		return string;
    	return null;
    }

}
